﻿#include "usersettings.h"
#include <QCryptographicHash>
#include <QDataStream>
#include <QDir>
#include <QFile>
#include <QSettings>
#include <QStandardPaths>
#include <QUuid>
#include "litedb.h"
#include "globaltools.h"

const QString UserSettings::sys_auto_login(QLatin1String("sys_auto_login"));
const QString UserSettings::sys_last_login_id(QLatin1String("sys_last_login_id"));
const QString UserSettings::sys_save_password(QLatin1String("sys_save_password"));
const QString UserSettings::sys_mic_index(QLatin1String("sys_mic_index"));
const QString UserSettings::sys_speaker_index(QLatin1String("sys_speaker_index"));
const QString UserSettings::sys_video_index(QLatin1String("sys_video_index"));
const QString UserSettings::sys_micphone_volume(QLatin1String("sys_micphone_volume"));
const QString UserSettings::sys_micphone_mute(QLatin1String("sys_micphone_mute"));
const QString UserSettings::sys_speaker_volume(QLatin1String("sys_speaker_volume"));
const QString UserSettings::sys_speaker_mute(QLatin1String("sys_speaker_mute"));
const QString UserSettings::sys_check_update_last(QLatin1String("sys_check_update_last"));
const QString UserSettings::sys_server_domain(QLatin1String("sys_server_domain"));
const QString UserSettings::sys_server_proxy(QLatin1String("sys_server_proxy"));
const QString UserSettings::sys_uuid(QLatin1String("sys_uuid"));
const QString UserSettings::sys_idpw_section(QLatin1String("IDPW"));
const QString UserSettings::sys_debug_file(QLatin1String("sys_debug_file"));

UserSettings::UserSettings():
personal_setting_typelist_({}),
system_setting_typelist_({
sys_auto_login,
sys_last_login_id,
sys_save_password,
sys_mic_index,
sys_speaker_index,
sys_video_index,
sys_micphone_volume,
sys_speaker_volume,
sys_check_update_last,
sys_server_domain,
sys_server_proxy})
{
    initSystemSettingFileName();
}

void UserSettings::get_personal_setting()
{
    QMutexLocker locker(&mutex_);
    if(personal_setting_fileName_.isEmpty())
    {
        if(setting_map.size()>0)
            foreach (const QString& key, personal_setting_typelist_)
            {
                setting_map.remove(key);
            }
        return;
    }
    QSettings perSetting(personal_setting_fileName_,QSettings::IniFormat);
    perSetting.setIniCodec("utf-8");
    QStringList keys = perSetting.allKeys();
    foreach (const QString& key, personal_setting_typelist_)
    {
        setting_map[key]=perSetting.value(key);
        keys.removeAll(key);
    }
    foreach (const QString& key, keys)
    {
        perSetting.remove(key);
    }
}

void UserSettings::save_personal_setting()
{
    QMutexLocker locker(&mutex_);
    if(personal_setting_fileName_.isEmpty())
        return;
    QSettings perSetting(personal_setting_fileName_,QSettings::IniFormat);
    perSetting.setIniCodec("utf-8");
    if(!perSetting.isWritable())
        return;
    foreach (const QString& key, personal_setting_typelist_)
    {
        QVariant value = setting_map.value(key);
        bool hasValue = !value.isNull();
        bool hasKey = !perSetting.value(key).isNull();
        if(hasValue)
            perSetting.setValue(key,value);
        else if(hasKey)
            perSetting.remove(key);
    }
}

void UserSettings::get_system_setting()
{
    QMutexLocker locker(&mutex_);
    if(system_setting_fileName_.isEmpty())
    {
        if(setting_map.size()>0)
            foreach (const QString& key, system_setting_typelist_)
            {
                setting_map.remove(key);
            }
        return;
    }
    QSettings sysSetting(system_setting_fileName_,QSettings::IniFormat);
    sysSetting.setIniCodec("utf-8");
    QStringList keys = sysSetting.allKeys();
    foreach (const QString& key, system_setting_typelist_)
    {
        setting_map[key]=sysSetting.value(key);
        keys.removeAll(key);
    }
    setting_map[sys_debug_file]=sysSetting.value(sys_debug_file);
    QVariant uuidFromIni = sysSetting.value(sys_uuid);
    keys.removeAll(sys_debug_file);
    keys.removeAll(sys_uuid);
    if(sysSetting.childGroups().contains(sys_idpw_section))
    {
        sysSetting.beginGroup(sys_idpw_section);
        QStringList IDs = sysSetting.allKeys();
        foreach (const QString& ID, IDs)
        {
            QString key = sys_idpw_section+'/'+ID;
            keys.removeAll(key);
            setting_map[key.trimmed()]=sysSetting.value(ID);
        }
        sysSetting.endGroup();
    }
    foreach (const QString& key, keys)
    {
        sysSetting.remove(key);
    }
    //check guid
    QVariant& uuid = setting_map[sys_uuid];
    QVariant hash;
    uuid.clear();
    bool isFromIni  = false;
    //read from ini
    QVariantList vl = uuidFromIni.toList();
    QVariant var = vl.value(0);
    QVariant nHash = vl.value(1);
    if(var.type()==QVariant::Uuid && !var.isNull() && qHash(var.toUuid())==nHash.toUInt())
    {
        uuid = var;
        hash = nHash;
        isFromIni = true;
    }
    else
    {
        //重新生成
        QUuid quid = QUuid::createUuid();
        uuid = quid;
        hash = qHash(quid);
    }
    if(!isFromIni)
    {
        QVariant v(QVariantList()<<uuid<<hash);
        if(v != uuidFromIni)
            sysSetting.setValue(sys_uuid, v);
    }
}

void UserSettings::save_system_setting()
{
    QMutexLocker locker(&mutex_);
    if(system_setting_fileName_.isEmpty())
        return;
    QSettings sysSetting(system_setting_fileName_,QSettings::IniFormat);
    sysSetting.setIniCodec("utf-8");
    if(!sysSetting.isWritable())
        return;
    foreach (const QString& key, system_setting_typelist_)
    {
        QVariant value = setting_map.value(key);
        bool hasValue = !value.isNull();
        bool hasKey = !sysSetting.value(key).isNull();
        if(hasValue)
            sysSetting.setValue(key,value);
        else if(hasKey)
            sysSetting.remove(key);
    }
}

void UserSettings::initSystemSettingFileName()
{
    system_setting_fileName_ = appDataDir() + QLatin1String("/sys_setting.ini");
    get_system_setting();
}

void UserSettings::resetPersonalSetting(const QString &uid)
{
    personal_setting_fileName_ = userDataDir(uid) + QLatin1String("/per_setting.ini");
    get_personal_setting();
}

bool UserSettings::getBool(const QString &key)
{
    QMutexLocker locker(&mutex_);
    return setting_map.value(key).toBool();
}

int UserSettings::getInt(const QString &key)
{
    QMutexLocker locker(&mutex_);
    return setting_map.value(key).toInt();
}

QString UserSettings::getString(const QString &key)
{
    QMutexLocker locker(&mutex_);
    return setting_map.value(key).toString();
}

QStringList UserSettings::getStringList(const QString &key)
{
    QMutexLocker locker(&mutex_);
    return setting_map.value(key).toStringList();
}

QVariant UserSettings::getValue(const QString &key)
{
    QMutexLocker locker(&mutex_);
    return setting_map.value(key);
}

void UserSettings::setValue(const QString &key, const QVariant &value)
{
    QMutexLocker locker(&mutex_);
    setting_map[key]=value;
}

bool UserSettings::setValueIfChanged(const QString &key, const QVariant &value)
{
    QMutexLocker locker(&mutex_);
    QVariant& v = setting_map[key];
    if (v == value)
        return false;
    v = value;
    return true;
}

QStringList UserSettings::getIDs()
{
    QStringList result;
    QMutexLocker locker(&mutex_);
    QStringList keys = setting_map.keys();
    QString prefix = sys_idpw_section+'/';
    foreach(const QString& key, keys)
    {
        if(key.startsWith(prefix))
        {
            QString id = key.mid(prefix.size());
            if(!id.isEmpty())
                result.append(id);
        }
    }
    return result;
}

QByteArray UserSettings::getHashedPW(const QString &id)
{
    QMutexLocker locker(&mutex_);
    return setting_map.value(sys_idpw_section+'/'+id).toByteArray();
}

void UserSettings::setPW(const QString &id, const QString &pw)
{
    QByteArray hashedPW;
    if(!pw.isEmpty())
    {
        hashedPW = QCryptographicHash::hash(pw.toUtf8(),QCryptographicHash::Md5);
    }
    setPW(id, hashedPW);
}

void UserSettings::setPW(const QString &id, const QByteArray &hashedPW)
{
    QVariant var(hashedPW);
    QMutexLocker locker(&mutex_);
    QString key = sys_idpw_section+'/'+id;
    QVariant& value = setting_map[key];
    if(value == var)
        return;
    value = var;
    if(system_setting_fileName_.isEmpty())
        return;
    QSettings sysSetting(system_setting_fileName_,QSettings::IniFormat);
    sysSetting.setIniCodec("utf-8");
    if(!sysSetting.isWritable())
        return;
    sysSetting.beginGroup(sys_idpw_section);
    sysSetting.setValue(id,var);
    sysSetting.endGroup();
}

void UserSettings::clearIDPW(const QString &id)
{
    QMutexLocker locker(&mutex_);
    QStringList keys = setting_map.keys();
    QString prefix = sys_idpw_section+'/';
    if(!id.isEmpty())
    {
        foreach(const QString& key, keys)
        {
            if(key==prefix+id)
            {
                setting_map.remove(key);
                if(setting_map[sys_last_login_id].toString()==id)
                    setting_map[sys_last_login_id]=QVariant();
                if(system_setting_fileName_.isEmpty())
                    return;
                QSettings sysSetting(system_setting_fileName_,QSettings::IniFormat);
                sysSetting.setIniCodec("utf-8");
                if(!sysSetting.isWritable())
                    return;
                if(sysSetting.value(sys_last_login_id).toString()==id)
                    sysSetting.remove(sys_last_login_id);
                if(sysSetting.childGroups().contains(sys_idpw_section))
                {
                    sysSetting.beginGroup(sys_idpw_section);
                    sysSetting.remove(id);
                    if(sysSetting.allKeys().isEmpty())
                    {
                        sysSetting.endGroup();
                        sysSetting.remove(sys_idpw_section);
                    }
                }
                return;
            }
        }
        return;
    }
    foreach(const QString& key, keys)
    {
        if(key.startsWith(prefix))
            setting_map.remove(key);
    }
    setting_map[sys_last_login_id]=QVariant();
    if(system_setting_fileName_.isEmpty())
        return;
    QSettings sysSetting(system_setting_fileName_,QSettings::IniFormat);
    sysSetting.setIniCodec("utf-8");
    if(!sysSetting.isWritable())
        return;
    sysSetting.remove(sys_last_login_id);
    if(sysSetting.childGroups().contains(sys_idpw_section))
        sysSetting.remove(sys_idpw_section);
}
